package utils

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"github.com/zeromicro/go-zero/core/logx"
	"io"
	"os"
	"strings"
)

/**
 * @Author: way lee
 * @Date: 2024/8/5 16:07
 * @Desc:
 */

// GenerateMD5AndSaveWithCustomRule 生成文件的 MD5 校验和并保存到指定文件，支持自定义规则
func GenerateMD5AndSaveWithCustomRule(filename string, md5File string, customString string) error {
	// 打开文件
	file, err := os.Open(filename)
	if err != nil {
		logx.Errorf("无法打开文件 %s: %v", filename, err)
		return fmt.Errorf("无法打开文件 %s: %v", filename, err)
	}
	defer file.Close()

	// 创建 MD5 哈希
	hash := md5.New()

	// 读文件内容并写入哈希
	if _, err := io.Copy(hash, file); err != nil {
		logx.Errorf("读取文件 %s 失败: %v", filename, err)
		return fmt.Errorf("读取文件 %s 失败: %v", filename, err)
	}

	// 加入自定义字符串到哈希
	hash.Write([]byte(customString))

	// 计算哈希值
	checksum := hash.Sum(nil)
	md5Checksum := hex.EncodeToString(checksum)

	// 将校验和写入文件
	return saveMD5ToFile(md5Checksum, md5File)
}

// saveMD5ToFile 将 MD5 校验和保存到文件
func saveMD5ToFile(checksum string, md5File string) error {
	file, err := os.Create(md5File)
	if err != nil {
		logx.Errorf("无法创建校验和文件 %s: %v", md5File, err)
		return fmt.Errorf("无法创建校验和文件 %s: %v", md5File, err)
	}
	defer file.Close()

	// 写入校验和到文件
	_, err = file.WriteString(fmt.Sprintf("%s  %s\n", checksum, md5File))
	if err != nil {
		logx.Errorf("写入校验和到文件时出错: %v", err)
		return fmt.Errorf("写入校验和到文件时出错: %v", err)
	}

	return nil
}

// VerifyMD5FromFile 验证文件的 MD5 校验和
func VerifyMD5FromFile(filename string, md5File string, customString string) (bool, error) {
	// 从文件中读取预期的校验和
	expectedChecksum, err := loadMD5FromFile(md5File)
	if err != nil {
		return false, err
	}

	// 生成实际的 MD5 校验和
	actualChecksum, err := GenerateMD5WithCustomRule(filename, customString)
	if err != nil {
		return false, err
	}

	// 返回校验结果
	return actualChecksum == expectedChecksum, nil
}

// GenerateMD5WithCustomRule 生成文件的 MD5 校验和，支持自定义规则
func GenerateMD5WithCustomRule(filename string, customString string) (string, error) {
	// 打开文件
	file, err := os.Open(filename)
	if err != nil {
		logx.Errorf("无法打开文件 %s: %v", filename, err)
		return "", fmt.Errorf("无法打开文件 %s: %v", filename, err)
	}
	defer file.Close()

	// 创建 MD5 哈希
	hash := md5.New()

	// 读文件内容并写入哈希
	if _, err := io.Copy(hash, file); err != nil {
		logx.Errorf("读取文件 %s 失败: %v", filename, err)
		return "", fmt.Errorf("读取文件 %s 失败: %v", filename, err)
	}

	// 加入自定义字符串到哈希
	hash.Write([]byte(customString))

	// 计算哈希值
	checksum := hash.Sum(nil)
	return hex.EncodeToString(checksum), nil
}

// loadMD5FromFile 从 MD5 文件中读取 MD5 值
func loadMD5FromFile(md5File string) (string, error) {
	data, err := os.ReadFile(md5File)
	if err != nil {
		logx.Errorf("无法读取校验和文件 %s: %v", md5File, err)
		return "", fmt.Errorf("无法读取校验和文件 %s: %v", md5File, err)
	}

	// 解析文件内容
	parts := strings.Fields(string(data))
	if len(parts) < 2 {
		logx.Errorf("校验和文件格式不正确")
		return "", fmt.Errorf("校验和文件格式不正确")
	}

	return parts[0], nil
}
